﻿// Copyright © Microsoft Corporation.  All Rights Reserved.
// This code released under the terms of the 
// Microsoft Public License (MS-PL, http://opensource.org/licenses/ms-pl.html.)

using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Effects;
using System.Windows.Threading;

namespace TheOliver.Controls
{
    public class ImageStack : Panel
    {
        public ImageStack()
        {
            _doubleClickTimer = new DispatcherTimer();
            _doubleClickTimer.Interval = new TimeSpan(0, 0, 0, 0, DoubleClickSpeedInMS);
            _doubleClickTimer.Tick += (s, e) =>
            {
                _clickCount = 0;
                _doubleClickTimer.Stop();
            };

            this.MouseLeftButtonUp += (s, e) =>
            {
                if (_clickCount == 1)
                {
                    // Doubleclick detected
                    HideCurrentImage();
                }
                else
                {
                    _clickCount++;
                    _doubleClickTimer.Start();
                }
            };

            _hideStoryboard.Completed += (s, e) =>
            {
                this.Children.Remove(_removedImage);
                this.Children.Insert(0, _removedImage);
                ShowNextImage();
            };

            _showStoryboard.Completed += (s, e) =>
            {
                _storyboardIsActive = false;
            };
        }

        Random _random = new Random();
        int _clickCount;
        DispatcherTimer _doubleClickTimer;
        Storyboard _hideStoryboard = new Storyboard();
        Storyboard _showStoryboard = new Storyboard();
        Image _removedImage;
        bool _storyboardIsActive = false;
        double _newX;
        double _newY;
        Size _finalSize;

        private void ShowNextImage()
        {
            TimeSpan duration = new TimeSpan(0, 0, 0, 0, AnimationInMS);

            // Part 2
            DoubleAnimation xa2 = new DoubleAnimation();
            xa2.Duration = duration;
            xa2.To = -_newX;

            DoubleAnimation ya2 = new DoubleAnimation();
            ya2.Duration = duration;
            ya2.To = -_newY;

            _showStoryboard.Stop();
            _showStoryboard.Children.Clear();
            _showStoryboard.Duration = duration;
            _showStoryboard.Children.Add(xa2);
            _showStoryboard.Children.Add(ya2);

            TransformGroup tg = _removedImage.RenderTransform as TransformGroup;
            TranslateTransform tt2 = new TranslateTransform();
            tg.Children.Add(tt2);
            _removedImage.RenderTransform = tg;

            Storyboard.SetTarget(xa2, tt2);   //set Animation Target
            Storyboard.SetTargetProperty(xa2, new PropertyPath("X"));   // set Animation TargetProperty
            Storyboard.SetTarget(ya2, tt2);
            Storyboard.SetTargetProperty(ya2, new PropertyPath("Y"));

            if (this.Resources.Contains("showme"))
            {
                this.Resources.Remove("showme");
            }
            this.Resources.Add("showme", _showStoryboard);

            _showStoryboard.Begin();
        }

        bool _layouted = false;

        protected override Size ArrangeOverride(Size finalSize)
        {
            _finalSize = finalSize;

            if (!_layouted)
            {
                LayoutImages(_finalSize);
                _layouted = true;
            }
            return base.ArrangeOverride(finalSize);
        }


        private void HideCurrentImage()
        {
            if (_storyboardIsActive)
            {
                return;
            }

            _storyboardIsActive = true;
            _removedImage = this.Children[this.Children.Count - 1] as Image;
            if (_removedImage != null)
            {
                TimeSpan duration = new TimeSpan(0, 0, 0, 0, AnimationInMS);

                double angle = _random.Next(360);
                _newX = Math.Sin(angle) * this.RenderSize.Width;
                _newY = Math.Cos(angle) * this.RenderSize.Height;

                _hideStoryboard.Stop();
                _hideStoryboard.Children.Clear();

                // Part 1
                DoubleAnimation xa1 = new DoubleAnimation();
                xa1.Duration = duration;
                xa1.To = _newX;

                DoubleAnimation ya1 = new DoubleAnimation();
                ya1.Duration = duration;
                ya1.To = _newY;

                _hideStoryboard.Duration = duration;
                _hideStoryboard.Children.Add(xa1);
                _hideStoryboard.Children.Add(ya1);

                TransformGroup tg = _removedImage.RenderTransform as TransformGroup;

                TranslateTransform tt = new TranslateTransform();
                tg.Children.Add(tt);
                _removedImage.RenderTransform = tg;

                Storyboard.SetTarget(xa1, tt);
                Storyboard.SetTargetProperty(xa1, new PropertyPath("X"));
                Storyboard.SetTarget(ya1, tt);
                Storyboard.SetTargetProperty(ya1, new PropertyPath("Y"));

                if (this.Resources.Contains("hideme"))
                {
                    this.Resources.Remove("hideme");
                }
                this.Resources.Add("hideme", _hideStoryboard);

                _hideStoryboard.Begin();
            }
        }

        private Size LayoutImages(Size finalSize)
        {
            var images = this.Children.OfType<Image>();
            Random rnd = new Random();

            foreach (var image in images)
            {
                // Drop Shadow Effect
                if (ShowDropShadowEffect)
                {
                    DropShadowEffect dse = new DropShadowEffect();
                    dse.ShadowDepth = 0.3;
                    image.Effect = dse;
                }

                // Center image
                image.VerticalAlignment = System.Windows.VerticalAlignment.Center;
                image.HorizontalAlignment = System.Windows.HorizontalAlignment.Center;

                TransformGroup tg = new TransformGroup();

                // Scale to fit the finalsize * 90%
                image.Stretch = Stretch.Uniform;
                image.Width = finalSize.Width * ResizeFactor;
                image.Height = finalSize.Height * ResizeFactor;

                image.Margin = new Thickness(
                    (finalSize.Width - image.Width) / 2, (finalSize.Height - image.Height) / 2,
                    (finalSize.Width - image.Width) / 2, (finalSize.Height - image.Height) / 2);

                // Rotate image
                RotateTransform rt = new RotateTransform();
                rt.Angle = 90 - rnd.Next(180);
                rt.CenterX = image.Width / 2;
                rt.CenterY = image.Height / 2;
                tg.Children.Add(rt);

                image.RenderTransform = tg;
            }
            return finalSize;
        }

        #region Properties


        public double ResizeFactor
        {
            get { return (double)GetValue(ResizeFactorProperty); }
            set { SetValue(ResizeFactorProperty, value); }
        }

        public static readonly DependencyProperty ResizeFactorProperty =
            DependencyProperty.Register(
                "ResizeFactor",
                typeof(double),
                typeof(ImageStack),
                new PropertyMetadata(0.5, OnValueChanged));

        private static void OnValueChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            ImageStack stack = sender as ImageStack;
            stack.InvalidateArrange();
        }

        public bool ShowDropShadowEffect
        {
            get { return (bool)GetValue(ShowDropShadowEffectProperty); }
            set { SetValue(ShowDropShadowEffectProperty, value); }
        }

        public static readonly DependencyProperty ShowDropShadowEffectProperty =
            DependencyProperty.Register(
                "ShowDropShadowEffect",
                typeof(bool),
                typeof(ImageStack),
                new PropertyMetadata(true, OnValueChanged));

        public int DoubleClickSpeedInMS
        {
            get { return (int)GetValue(DoubleClickSpeedInMSProperty); }
            set { SetValue(DoubleClickSpeedInMSProperty, value); }
        }

        public static readonly DependencyProperty DoubleClickSpeedInMSProperty =
            DependencyProperty.Register(
                "DoubleClickSpeedInMS",
                typeof(int),
                typeof(ImageStack),
                new PropertyMetadata(500, OnDoubleClickSpeedChanged));

        private static void OnDoubleClickSpeedChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            ImageStack stack = sender as ImageStack;
            stack._doubleClickTimer.Stop();
            stack._doubleClickTimer.Interval = new TimeSpan(0, 0, 0, 0, (int)e.NewValue);
        }

        public bool EnableDragging
        {
            get { return (bool)GetValue(EnableDraggingProperty); }
            set { SetValue(EnableDraggingProperty, value); }
        }

        public static readonly DependencyProperty EnableDraggingProperty =
            DependencyProperty.Register(
                "EnableDragging",
                typeof(bool),
                typeof(ImageStack),
                new PropertyMetadata(false));

        public int AnimationInMS
        {
            get { return (int)GetValue(AnimationInMSProperty); }
            set { SetValue(AnimationInMSProperty, value); }
        }

        public static readonly DependencyProperty AnimationInMSProperty =
            DependencyProperty.Register(
                "AnimationInMS",
                typeof(int),
                typeof(ImageStack),
                new PropertyMetadata(125));

        #endregion

    }
}
